package com.begood.leishi.fingerprinter.controller;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.support.annotation.NonNull;
import android.util.Log;

import com.begood.leishi.fingerprinter.FpConfig;
import com.begood.leishi.fingerprinter.FingerPrinter;
import com.begood.leishi.fingerprinter.FpError;
import com.begood.leishi.fingerprinter.Mcro;
import com.begood.leishi.fingerprinter.R;
import com.begood.leishi.fingerprinter.dao.DaoMaster;
import com.begood.leishi.fingerprinter.dao.DaoSession;
import com.begood.leishi.fingerprinter.dao.User;
import com.begood.leishi.fingerprinter.dao.UserDao;
import com.begood.leishi.fingerprinter.utils.Slog;
import com.za.finger.ZA_finger;
import com.za.finger.ZAandroid;

import org.greenrobot.greendao.database.Database;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * Created by leishi on 2017/11/30.
 */

public class Controller  {

    Context mContext;
    private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
    private UsbManager mDevManager = null;
    private PendingIntent permissionIntent = null;
    private UsbInterface intf = null;
    private UsbDeviceConnection connection = null;
    private UsbDevice device = null;
    private FingerPrinter.openUsbListener mOpenUsbListener = null;
    Slog log;
    private UserDao userDao;
    //指纹模块库
    private ZAandroid fingerSo;
    //接口类型
    //(1)串口com 3.0(2)usb 3.0 (4)串口 4.0(5)usb 4.0
    private final int defDeviceType = 5;

    private final int defiCom=3;//3
    private final int defiBaud=6;
    //没有手指标志位
    private boolean noFinger = false;
    //设备已经成功打开标识
    private boolean deviceOpened = false;
    //指纹模块需要的地址
    private int DEV_ADDR = 0xffffffff;
    //按指纹的次数
    private int pressCounter = 0;
    //接收数据包失败的次数
    private int errorPacket = 0;

    //找到的指纹的ID
    private long fingerID = -1;

    public Controller(Context context){
        mContext = context;
        log = new Slog(mContext);
        init();
        dbInit();
    }
    private void init(){
        fingerSo = new ZAandroid();
    }
    //数据库初始化
    private void dbInit(){
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(mContext, FpConfig.ENCRYPTED
                ? FpConfig.DB_NAME_ENCRYPTED : FpConfig.DB_NAME);
        //有无密码的数据库创建
        Database db = FpConfig.ENCRYPTED ? helper.getEncryptedWritableDb(FpConfig.DB_PASSWORD) : helper.getWritableDb();
        DaoSession daoSession = new DaoMaster(db).newSession();
        userDao = daoSession.getUserDao();
    }

    //开启端口
    public void openUsb(@NonNull FingerPrinter.openUsbListener l) throws IOException{
        ZA_finger fppower = new ZA_finger();
        fppower.finger_power_on();
        mOpenUsbListener = l;
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                try {
                    getUsb(emitter);
                }catch (IOException e){
                    throw  new IOException(mContext.getString(R.string.no_fingerprinter_device));
                }
            }
        }).subscribeOn(Schedulers.io()) //指定 subscribe() 发生在 IO 线程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d){

            }
            @Override
            public void onNext(String value) {
            }
            @Override
            public void onComplete() {
                openDevice();
            }

            @Override
            public void onError(Throwable e) {
                deviceOpened = false;
            }
        });
    }

    private void getUsb(ObservableEmitter<String> emitter) throws IOException{

        mDevManager = ((UsbManager) mContext.getSystemService(Context.USB_SERVICE));
        permissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        mContext.registerReceiver(mUsbReceiver, filter);
        //this.registerReceiver(mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED));
        HashMap<String, UsbDevice> deviceList = mDevManager.getDeviceList();
        for (UsbDevice tdevice : deviceList.values()) {
            if (tdevice.getVendorId() == 0x2109 && (tdevice.getProductId() == 0x7638))
            {
                device = tdevice;
                if (!mDevManager.hasPermission(device)) {
                    mDevManager.requestPermission(tdevice, permissionIntent);
                }else {
                    emitter.onComplete();
                }
                return;
            }
        }
        deviceOpened = false;
        throw  new IOException();
    }

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            context.unregisterReceiver(mUsbReceiver);
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (context) {
                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true)) {
                        openDevice();
                    }else {
                        deviceOpened = false;
                        Throwable e = new Throwable(mContext.getString(R.string.no_usb_permission));
                        mOpenUsbListener.onFailure(e.getMessage());
                    }
                }
            }
        }
    };
    private void openDevice(){
        Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                connection = mDevManager.openDevice(device);
                if (!connection.claimInterface(device.getInterface(0), true)){
                    emitter.onError(new Throwable());
                    return;
                }
                if (device.getInterfaceCount() < 1) {
                    emitter.onError(new Throwable());
                    return;
                }
                intf = device.getInterface(0);
                if (intf.getEndpointCount() == 0){
                    emitter.onError(new Throwable());

                    return;
                }
                if (connection == null) {
                    emitter.onError(new Throwable());
                    return;
                }
                int fd = connection.getFileDescriptor();
                int status = fingerSo.ZAZOpenDeviceEx(fd, defDeviceType, defiCom, defiBaud
                        , 0, 0);
                byte[] pPassword = new byte[4];
                int pwd=fingerSo.ZAZVfyPwd(DEV_ADDR, pPassword);
                if(status == 1 && pwd == 0){
                    emitter.onComplete();
                }else {
                    ZA_finger fppower1 = new ZA_finger();
                    fppower1.finger_power_off();
                    emitter.onError(new Throwable());
                    return;
                }
            }
        }).subscribeOn(Schedulers.io()) //指定 subscribe() 发生在 IO 线程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<String>() {
                    @Override
                    public void onSubscribe(Disposable d){

                    }
                    @Override
                    public void onNext(String value) {
                    }
                    @Override
                    public void onComplete() {
                        deviceOpened = true;
                        mOpenUsbListener.onSuccess();
                    }

                    @Override
                    public void onError(Throwable e) {
                        deviceOpened = false;
                        mOpenUsbListener.onFailure("打开指纹模块时发生错误");
                    }
                });
    }

    //注册指纹
    public void registerFinger(@NonNull final FingerPrinter.fingerListener l,final String userName){
        if(!deviceOpened){
            l.onFailure(-10);
            return;
        }
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                int ret;
                long startTime = System.currentTimeMillis();
                noFinger = false;
                while (System.currentTimeMillis() - startTime < FpConfig.FINGER_TIMEOUT) {
                    //查看是否存在手指
                    ret = fingerSo.ZAZGetImage(DEV_ADDR);
                    try {
                        Thread.sleep(FpConfig.FINGER_INTERVAL);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (ret == 0) {
                        //防抖
                        ret = fingerSo.ZAZGetImage(DEV_ADDR);
                    }
                    if (ret == 0) {

                        //指纹需要确认两次
                        if (pressCounter == 0) {
                            pressCounter++;
                            noFinger = false;
                            //保存到第一个buffer
                            ret= fingerSo.ZAZGenChar(DEV_ADDR,1);
                            if(ret == 0) {
                                int[] id_iscore = new int[1];
                                //保证该指纹没有被注册过
                                ret = fingerSo.ZAZHighSpeedSearch(DEV_ADDR, 1, 0, 1000, id_iscore);
                                if(ret == 9) {
                                    startTime = System.currentTimeMillis();
                                    emitter.onNext(Mcro.STATUS_REGISTER_AGAIN);
                                    continue;
                                }else if(ret == 0){
                                    Throwable e = new Throwable(FpError.message(0x22));
                                    emitter.onError(e);
                                    return;
                                }else {
                                    Throwable e = new Throwable(FpError.message(ret));
                                    emitter.onError(e);
                                    return;
                                }

                            }
                        }else{
                            //抬手再次录入指纹
                            if(! noFinger){
                                continue;
                            }
                            noFinger = false;
                            pressCounter = 0;
                            ret= fingerSo.ZAZGenChar(DEV_ADDR,2);
                            if(ret == 0) {
                                //两次按的指纹合成
                                ret= fingerSo.ZAZRegModule(DEV_ADDR);
                                if(ret == 0) {
                                    //实际上是从服务器获取ID
                                    int tempID = (int)(Math.random()*1000);
                                    while (getNameByIDFromDB(tempID) != null){
                                        tempID = (int)(Math.random()*1000);
                                    }
                                    //dat文件名 ID
                                    String datName = mContext.getExternalFilesDir("") +"/"+tempID;

                                    ret = fingerSo.ZAZUpChar2File(DEV_ADDR, 1,datName.getBytes());
                                    if(ret == 0){
                                        //上传datName文件到服务器
                                        saveUserSql(tempID,userName);

                                        ret = fingerSo.ZAZStoreChar(DEV_ADDR, 1, tempID);
                                        if(ret == 0){
                                            emitter.onComplete();
                                            return;
                                        }
                                    }
                                    Throwable e = new Throwable(FpError.message(ret));
                                    emitter.onError(e);
                                    return;
                                }
                            }
                        }

                        if(ret != 0) {
                            Throwable e = new Throwable(FpError.message(ret));
                            emitter.onError(e);
                            return;
                        }

                    } else if (ret == 2 ) {//当没有手指按在指纹模块上
                        if( !noFinger){
                            emitter.onNext(Mcro.STATUS_NO_FINGER);
                            noFinger = true;
                        }
                    }else if (ret == -2) {//接收数据包失败
                        if((errorPacket++)>3){
                            Throwable e = new Throwable(FpError.message(ret));
                            emitter.onError(e);
                            return;
                        }
                    }else if (ret == 3) {//录入指纹图象失败
                        Throwable e = new Throwable(FpError.message(ret));
                        emitter.onError(e);
                        return;
                    }else{
                        Throwable e = new Throwable(FpError.message(ret));
                        emitter.onError(e);
                        return;
                    }
                }
                //超时
                Throwable e = new Throwable(FpError.message(0x21));
                emitter.onError(e);
                return;
            }
        }).subscribeOn(Schedulers.io()) //指定 subscribe() 发生在 IO 线程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Integer>() {
                    Disposable disposable;
                    @Override
                    public void onSubscribe(Disposable d){
                        disposable = d;
                    }
                    @Override
                    public void onNext(Integer value) {
                        l.onNext(value);
                    }
                    @Override
                    public void onComplete() {
                        pressCounter = 0;
                        noFinger = false;
                        errorPacket = 0;
                        l.onSuccess((int)getIDByNameFromDB(userName));
                    }

                    @Override
                    public void onError(Throwable e) {
                        pressCounter = 0;
                        noFinger = false;
                        errorPacket = 0;
                        l.onFailure(FpError.code(e.getMessage()));
                    }
                });
    }
    public void matchFinger(@NonNull final FingerPrinter.fingerListener l){
        if(!deviceOpened){
            l.onFailure(-10);
            return;
        }
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                int ret;
                long startTime = System.currentTimeMillis();
                noFinger = false;
                while (System.currentTimeMillis() - startTime < FpConfig.FINGER_TIMEOUT) {
                    //查看是否存在手指
                    ret = fingerSo.ZAZGetImage(DEV_ADDR);
                    try {
                        Thread.sleep(FpConfig.FINGER_INTERVAL);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (ret == 0) {
                        //防抖
                        ret = fingerSo.ZAZGetImage(DEV_ADDR);
                    }
                    if (ret == 0) {
                        ret= fingerSo.ZAZGenChar(DEV_ADDR,1);
                        if(ret == 0) {
                            int[] id_iscore = new int[1];
                            ret = fingerSo.ZAZHighSpeedSearch(DEV_ADDR, 1, 0, 1000, id_iscore);
                            if(ret == 0) {
                                fingerID = (long)id_iscore[0];
                                emitter.onComplete();
                                return;
                            }
                        }
                        Throwable e = new Throwable(FpError.message(ret));
                        emitter.onError(e);
                        return;
                    } else if (ret == 2 ) {//当没有手指按在指纹模块上
                        if(!noFinger){
                            emitter.onNext(Mcro.STATUS_NO_FINGER);
                            noFinger = true;
                        }
                    }else if (ret == -2) {//接收数据包失败
                        if((errorPacket++)>3){
                            Throwable e = new Throwable(FpError.message(ret));
                            emitter.onError(e);
                            return;
                        }
                    }else{
                        Throwable e = new Throwable(FpError.message(ret));
                        emitter.onError(e);
                        return;
                    }
                }
                //超时
                Throwable e = new Throwable(FpError.message(0x21));
                emitter.onError(e);
                return;
            }
        }).subscribeOn(Schedulers.io()) //指定 subscribe() 发生在 IO 线程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Integer>() {
                    Disposable disposable;
                    @Override
                    public void onSubscribe(Disposable d){
                        disposable = d;
                    }
                    @Override
                    public void onNext(Integer value) {
                        l.onNext(value);
                    }
                    @Override
                    public void onComplete() {
                        noFinger = false;
                        errorPacket = 0;
                        if(fingerID == -1){
                            l.onFailure(0xff);
                        }else {
                            l.onSuccess((int) fingerID);
                        }
                        fingerID = -1;
                    }

                    @Override
                    public void onError(Throwable e) {
                        noFinger = false;
                        errorPacket = 0;
                        l.onFailure(FpError.code(e.getMessage()));
                    }
                });
    }

    public void deleteAll(@NonNull final FingerPrinter.fingerListener l){
        Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
                int ret;
                ret = fingerSo.ZAZEmpty(DEV_ADDR);
                if (ret != 0) {
                    Throwable e = new Throwable(FpError.message(ret));
                    emitter.onError(e);
                    return;
                }

                userDao.deleteAll();
                //删除所有文件
                File directory = new File(mContext.getExternalFilesDir("")+"");
                File[] files = directory.listFiles();
                if (files != null && files.length != 0) {
                    for (File f : files) {
                        f.delete();
                    }
                }
                emitter.onComplete();
            }
        }).subscribeOn(Schedulers.io()) //指定 subscribe() 发生在 IO 线程
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Integer>() {
                    Disposable disposable;
                    @Override
                    public void onSubscribe(Disposable d){
                        disposable = d;
                    }
                    @Override
                    public void onNext(Integer value) {
                    }
                    @Override
                    public void onComplete() {
                            l.onSuccess(0);
                    }

                    @Override
                    public void onError(Throwable e) {
                        l.onFailure(FpError.code(e.getMessage()));
                    }
                });
    }

    public void saveUserSql(long id ,String userName){
        User userInfo = new User();
        userInfo.setId(id);
        userInfo.setName(userName);
        long aa = userDao.insert(userInfo);
        log.log("aa:"+aa);
    }

    public String getNameByIDFromDB(long id){
        List<User> userList = userDao.queryBuilder().where(UserDao.Properties.Id.eq(id)).build().list();
        if(userList == null || userList.size() == 0){
            return null;
        }else {
            return userList.get(0).getName();
        }
    }
    public long getIDByNameFromDB(String name){
        List<User> userList = userDao.queryBuilder().where(UserDao.Properties.Name.eq(name)).build().list();
        if(userList == null || userList.size() == 0){
            return -1;
        }else {
            return userList.get(0).getId();
        }
    }
    public void closeDevice(){
        fingerSo.ZAZCloseDeviceEx();
        ZA_finger fppower = new ZA_finger();
        fppower.finger_power_off();
    }
    @Override
    protected void finalize() throws Throwable {
        log.free();
        super.finalize();
    }
}
